package com.study.utils;

import java.util.Map;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * 
 * @描述: 简单实现redis连接池
 * @版权: Copyright (c) 2020 
 * @公司: 
 * @作者: 严磊
 * @版本: 1.0 
 * @创建日期: 2020年6月17日 
 * @创建时间: 下午9:02:53
 */
public class RedisSimplePool
{
    private static Logger logger = Logger.getLogger(RedisSimplePool.class);
    
    private static JedisPool pool = null;
    
    public static void initPool() throws Exception
    {
        if(pool == null)
        {
            //读取redis 数据库配置文件
            Map<String, String> dataSourceConfig = ReadCofnigFileUtils.readProperties("config/redis_config.properties");
            
            JedisPoolConfig config = new JedisPoolConfig();
            //连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true。只有当值为true时，下面的maxWaitMillis才会生效
            config.setBlockWhenExhausted(true);
            //当资源池连接用尽后，获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
            config.setMaxWaitMillis(-1);
            
            //设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
            config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
            
            //当从池中获取资源或者将资源还回池中时 是否使用java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平锁机制,默认为false
            config.setFairness(false);
            
            //是否开启JMX监控，默认true建议开启，请注意应用本身也需要开启。
            config.setJmxEnabled(true);
            //设置JMX基础名，默认为null
            config.setJmxNameBase(null);
            //设置JMX前缀名,默认值pool
            config.setJmxNamePrefix("pool");
            
            //是否启用后进先出, 默认true
            config.setLifo(true);
            
            //最大空闲连接数, 默认8个
            config.setMaxIdle(Integer.parseInt(dataSourceConfig.getOrDefault("maxIdle","8")));
            //最小空闲连接数, 默认0
            config.setMinIdle(0);
            
            //最大连接数, 默认8个
            config.setMaxTotal(Integer.parseInt(dataSourceConfig.getOrDefault("maxTotal","8"))); 
            
            
            //向资源池获取连接的时候检查有效性（ping）, 默认false。检测到无效连接将会被移除。业务量很大时候建议设置为false，减少一次ping的开销。
            config.setTestOnBorrow(false);
            //向资源池归还连接时是否做连接有效性检测（ping）, 默认false。检测到无效连接将会被移除。业务量很大时候建议设置为false，减少一次ping的开销。
            config.setTestOnReturn(false);
            //在连接对象创建时测试连接对象的有效性,默认false
            config.setTestOnCreate(false);
            
            //是否开启空闲资源检测，在空闲时检查有效性, 默认false，建议设置为true
            config.setTestWhileIdle(true);
            //空闲资源的检测周期（单位为毫秒） 如果为负数,则不运行逐出线程, 默认-1
            config.setTimeBetweenEvictionRunsMillis(-1);
            //做空闲资源检测时，每次检测资源的个数 如果为负数就是 : 1/abs(n), 默认3
            config.setNumTestsPerEvictionRun(3);
            
            //资源池中资源的最小空闲时间（单位为毫秒），达到此值后空闲资源将被移除。逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
            config.setMinEvictableIdleTimeMillis(1800000);
            //对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)   
            config.setSoftMinEvictableIdleTimeMillis(1000L * 60L * 30L);
            
            
            //普通非集群模式
            String host = dataSourceConfig.getOrDefault("host", "127.0.0.1");
            int port = Integer.parseInt(dataSourceConfig.getOrDefault("port", "6379"));
            int timeout = Integer.parseInt(dataSourceConfig.getOrDefault("timeout", "2000"));
            String password = dataSourceConfig.get("password");
            pool = new JedisPool(config, host, port, timeout, password, false);
        }
    }
    
    public static Jedis getJedis()
    {
        try
        {
            initPool();
        }
        catch (Exception e)
        {
            logger.error("初始化redis连接池出错", e);
        }
        return pool.getResource();
    }
    
    /**
     * 返回连接到连接池
     */
    public static void returnJedis(Jedis conn)
    {
        if (conn != null)
        {
            //重池子获取Jedis连接的时候会将池set到Jedis的dataSource对象上
            //Jedis 的close方法会调用连接池pool的returnBrokenResource或者returnResource方法将连接返回给池
            conn.close(); 
        } 
    }
}

